package com.googlecode.jexnihilo;

import com.googlecode.totallylazy.*;
import com.googlecode.totallylazy.regex.Regex;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.googlecode.totallylazy.Sequences.sequence;

public class ContentReplacingSource implements Sources {
	private final Pattern pattern;
	private final String replacement;
	private final Sources decorated;
	private final Sequence<Regex> excluedFilePatterns;

	public ContentReplacingSource(String pattern, String replacement, Sources decorated, Sequence<String> excludeFilePatterns) {
		this.pattern = Pattern.compile(pattern);
		this.replacement = replacement;
		this.decorated = decorated;
		this.excluedFilePatterns = excludeFilePatterns.map(regex());
	}

	public static Sources replaceContent(String from, String to, Sources decorated, String... filePatterns) {
		return new ContentReplacingSource(from, to, decorated, sequence(filePatterns));
	}

	@Override
	public Sequence<Source> sources() {
		return decorated.sources().map(replaceContent());
	}

	private Callable1<? super Source, ? extends Source> replaceContent() {
		return new Callable1<Source, Source>() {
			@Override
			public Source call(Source source) throws Exception {
				if(!shouldProcess(source)) return source;
				return new Source(source.name, source.modified, applyRegex(source.input));
			}
		};
	}

	private InputStream applyRegex(InputStream input) {
		String content = Strings.toString(input);
		Matcher matcher = pattern.matcher(content);
		String processed = matcher.replaceAll(replacement);
		return new ByteArrayInputStream(processed.getBytes());
	}

	private boolean shouldProcess(Source source) {
		return !excluedFilePatterns.exists(Predicates.<CharSequence>matches(source.name));
	}

	private static Function1<String, Regex> regex() {
		return new Function1<String, Regex>() {
			@Override
			public Regex call(String s) throws Exception {
				return Regex.regex(s);
			}
		};
	}

	@Override
	public void close() throws IOException {
		decorated.close();
	}
}
